package com.jdeng.dubbo.spi;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MyExtensionLoader<T>  {

    // key is name , Class is impl
    private final Map<String, Class<?>> cachedClasses = new ConcurrentHashMap<>();


    private final Class<?> type;

    private MyExtensionLoader(Class<?> type) {
        this.type = type;
    }

    public static <T> MyExtensionLoader<T> getExtensionLoader(Class<T> type) {

        if (type == null) {
            throw new IllegalArgumentException("Extension type == null");
        }
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type (" + type + ") is not an interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type (" + type +
                    ") is not an extension, because it is NOT annotated with @" + MySPI.class.getSimpleName() + "!");
        }

        return new MyExtensionLoader<T>(type);
    }

    private static <T> boolean withExtensionAnnotation(Class<T> type) {
        return type.isAnnotationPresent(MySPI.class);
    }

    public T getExtension(String name) throws Exception {

        Class<?> aClass = cachedClasses.get(name);

        if (aClass == null) {
            generateClass();
        }

        aClass = cachedClasses.get(name);
        if (aClass == null) {
            throw new Exception("not find the class for this name: " + name);
        }

        Object obj = aClass.newInstance();;

        return (T)obj;
    }

    private void generateClass() throws Exception {
        String fileName = "META-INF/myself/" + this.type.getName();
        Enumeration<URL> urls = MyExtensionLoader.class.getClassLoader().getResources(fileName);
        if (urls != null) {
            while (urls.hasMoreElements()) {
                java.net.URL resourceURL = urls.nextElement();
                loadResource(cachedClasses, resourceURL);
            }
        }
    }

    private void loadResource(Map<String, Class<?>> cachedClasses, URL resourceURL) throws Exception{

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(resourceURL.openStream(), StandardCharsets.UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                final int ci = line.indexOf('#');
                if (ci >= 0) {
                    line = line.substring(0, ci);
                }
                line = line.trim();
                if (line.length() > 0) {
                    try {
                        String name = null;
                        int i = line.indexOf('=');
                        if (i > 0) {
                            name = line.substring(0, i).trim();
                            line = line.substring(i + 1).trim();
                        }
                        if (line.length() > 0) {
                            cachedClasses.put(name, Class.forName(line));
                        }
                    } catch (Throwable t) {
                        IllegalStateException e = new IllegalStateException("Failed to load extension class (interface: " + type + ", class line: " + line + ") in " + resourceURL + ", cause: " + t.getMessage(), t);
                    }
                }
            }
        } catch (Throwable t) {
            throw t;
        }
    }
}
